package com.jetpack.mvvm.lib.net.factory

import com.jetpack.mvvm.lib.net.anno.MockData
import com.jetpack.mvvm.lib.net.module.ApiResp
import com.jetpack.mvvm.lib.net.module.HttpApiResult
import okhttp3.Request
import okio.Timeout
import retrofit2.*
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type

class SuspendCallAdapterFactory : CallAdapter.Factory() {
    override fun get(returnType: Type, annotations: Array<out Annotation>, retrofit: Retrofit): CallAdapter<*, *>? {
        // 从 HttpServiceMethod 源码可以看到，retrofit 会对 suspend 的方法解析到的 annotations 数据手动插入 SkipCallbackExecutor 到首个元素，
        // 通过 annotations[0] is SkipCallbackExecutor 从而可以判断当前方法是否是协程的 suspend 调用。但这个有风险，需要跟进 retrofit 后期的版本
        var isKotlinSuspendFunction = false
        var mockConfig: MockConfig? = null
        for (annotation in annotations) {
            //解析 mock 配置信息
            if (annotation is MockData) {
                mockConfig = MockConfig(isMockData = true, mockUrl = annotation.mockUrl)
                break
            }
        }
        if (annotations.size > 1 && annotations[0] is SkipCallbackExecutor) {
            isKotlinSuspendFunction = true
        }
        if (!isKotlinSuspendFunction) {
            return null
        }
        //协程挂起函数默认返回值是Call<*>
        if (getRawType(returnType) != Call::class.java) {
            return null
        }
        check(returnType is ParameterizedType) {
            "return type must be something like ApiResult<*> or ApiResult<out *> for Call<*> check"
        }
        val responseType = getParameterUpperBound(0, returnType)
        if (getRawType(responseType) != HttpApiResult::class.java) {
            return null
        }
//        if (mockConfig != null && mockConfig.isMockData && mockConfig.dataPath.isNotBlank()) {
//            return MockCallAdapter<ApiResp<String>, String>()
//        } else {
            return when (responseType) {
                is ParameterizedType -> {
                    val bodyType = getParameterUpperBound(0, responseType)
                    SuspendCallAdapter<ApiResp<Any?>, Any?>(bodyType, mockConfig)
                }
                else -> {
                    SuspendCallAdapter<ApiResp<Any?>, Any?>(Nothing::class.java, mockConfig)
                }
            }
//        }
    }


    companion object {
        private const val TAG = "SuspendCallAdapterFactory"
        fun create(): SuspendCallAdapterFactory {
            return SuspendCallAdapterFactory()
        }
    }
}

class MockCallAdapter<R: ApiResp<String>, T: String>(): CallAdapter<R, Call<HttpApiResult<T>>> {
    override fun responseType(): Type {
        return String::class.java
    }

    override fun adapt(call: Call<R>): Call<HttpApiResult<T>> {
        TODO("Not yet implemented")
    }

}


class SuspendCallAdapter<R : ApiResp<T>, T : Any?>(private val bodyType: Type, val mockConfig: MockConfig? = null) :
    CallAdapter<R, Call<HttpApiResult<T>>> {
    override fun responseType(): Type {
        return assembleApiRespType(bodyType)
    }

    override fun adapt(call: Call<R>): Call<HttpApiResult<T>> {
        return if (mockConfig != null && mockConfig.isMockData && mockConfig.mockUrl.isNotBlank()) {
            SuspendHttpCall(mockUrl(call, mockConfig.mockUrl))
        } else {
            SuspendHttpCall(call)
        }
    }
}

//internal class MockSuspendHttpCall<>()

internal class SuspendHttpCall<R : ApiResp<T>, T : Any?>(private val delegate: Call<R>) :
    Call<HttpApiResult<T>> {
    override fun clone(): Call<HttpApiResult<T>> {
        return SuspendHttpCall(delegate.clone())
    }

    override fun execute(): Response<HttpApiResult<T>> {
        throw UnsupportedOperationException("NetworkResponseCall doesn't support execute")
    }

    override fun enqueue(callback: Callback<HttpApiResult<T>>) {
        println(delegate.javaClass)

        delegate.enqueue(object : Callback<R> {
            override fun onResponse(call: Call<R>, response: Response<R>) {
                val body = response.body()
                var httpApiResult: HttpApiResult<T>? = null
                if (response.isSuccessful) {
                    body?.data?.apply {
                        httpApiResult = HttpApiResult.Success(this, response.headers())
                    } ?: kotlin.run {
                        httpApiResult = HttpApiResult.unknownError(
                            throwable = IllegalArgumentException("response data is invalid"),
                            responseHeader = response.headers()
                        )
                    }
                    callback.onResponse(this@SuspendHttpCall, Response.success(httpApiResult))
                } else {
                    onFailure(call, HttpException(response))
                }
            }

            override fun onFailure(call: Call<R>, t: Throwable) {
                var statusCode = -1
                var message: String? = ""
                if (t is HttpException) {
                    message = t.message
                    statusCode = t.code()
                    val result: HttpApiResult<T> =
                        HttpApiResult.apiError(code = statusCode, message = message, throwable = t, responseHeader = null)
                    callback.onResponse(this@SuspendHttpCall, Response.success(result))
                } else {
                    val result: HttpApiResult<T> = HttpApiResult.networkError(error = t, responseHeader = null)
                    callback.onResponse(this@SuspendHttpCall, Response.success(result))
                }

            }

        })
    }

    override fun isExecuted(): Boolean {
        return delegate.isExecuted
    }

    override fun cancel() {
        delegate.cancel()
    }

    override fun isCanceled(): Boolean {
        return delegate.isCanceled
    }

    override fun request(): Request {
        return delegate.request()
    }

    override fun timeout(): Timeout {
        return delegate.timeout()
    }
//
//    fun generateHttpResult(
//        t: Throwable,
//        statusCode: Int
//    ): HttpApiResult<Nothing> {
//        if (isApiError(t, statusCode)) {
//            return HttpApiResult.apiError(
//                code = statusCode
//                meta?.message,
//                t,
//                parseHeader(t),
//            )
//        }
//        if (isNonNetwork(t)) {
//            return HttpApiResult.NetworkError(t)
//        }
//        return HttpApiResult.UnknownError(t)
//    }
}

class MockConfig(val isMockData: Boolean, val mockUrl: String)